package 遍历器;

import 作用域相关.*;
import 抽象语法树节点.*;
import 抽象语法树节点.后缀表达式.*;
import 抽象语法树节点.字面量类.*;
import 抽象语法树节点.表达式类.*;
import 抽象语法树节点.语句类.*;

public class 引用监听器 extends 抽象语法树基础监听器 {

    public 引用监听器(符号表 符号表) {
        this.符号表 = 符号表;
    }

    private final 符号表 符号表;
    public final 日志 日志 = new 日志();


    @Override
    public void 离开父类(父类 节点) {
        var 类符号 = (类符号) 节点.作用域;
        var 父类符号 = 节点.作用域.解析(节点.名称);
        if (父类符号 instanceof 类符号 父类) {
            类符号.父类 = 父类;
            日志.信息("行 " + 节点.idToken.getLine() + " 解析父类 " + 节点.名称);
            return;
        }

        日志.错误("行 " + 节点.idToken.getLine() + " 找不到父类 " + 节点.名称);
    }

    @Override
    public void 离开方法定义(方法定义 节点) {
        var 方法符号 = 节点.符号;
        var 返回值 = 节点.作用域.解析(节点.返回值类型.名称);
        方法符号.类型 = (类型) 返回值; // 这个求值类型在遍历到 [类型引用] 的时候会赋值
    }

    @Override
    public void 离开变量定义(变量定义 节点) {
        if (节点.初始值 != null) {
            日志.信息("行 " + 节点.idToken.getLine() + " 初始值求值类型 " + 节点.初始值.求值类型);
            符号表.赋初值(节点, 节点.初始值);
        }
    }

    @Override
    public void 离开指针定义(指针定义 节点) {
        if (节点.初始值 != null) {
            日志.信息("行 " + 节点.idToken.getLine() + " 初始值求值类型 " + 节点.初始值.求值类型);
            符号表.赋初值(节点, 节点.初始值); // todo: 感觉这里可以针对符号表进行一轮测试用例，可以加深理解
        }
    }


    @Override
    public void 离开类型引用(类型引用 节点) {
        var 类型 = 节点.作用域.解析(节点.名称);

        if (类型 instanceof 类型 目标类型) {
            日志.信息("行 " + 节点.idToken.getLine() + " 解析类型 " + 节点.名称);
            节点.求值类型 = 目标类型;
        } else {
            日志.错误("行 " + 节点.idToken.getLine() + " 找不到类型 " + 节点.名称);
        }
    }


    @Override
    public void 离开条件语句(条件语句 节点) {
        符号表.条件语句(节点);
    }


    @Override
    public void 离开返回语句(返回语句 节点) {
        // 返回语句主要检查的是返回的类型与方法的返回值是否一致

        var 方法节点 = 符号表.取方法符号(节点.作用域).节点;
        if (节点.返回值 == null) {
            方法节点.求值类型 = 类型索引.空.类型;
        } else {
            方法节点.求值类型 = 节点.求值类型;
        }

        日志.信息("返回语句 求值类型: " + 节点.求值类型);

        符号表.返回语句(符号表.取方法符号(节点.作用域), 节点.返回值);
    }


    @Override
    public void 离开赋值语句(赋值语句 节点) {
        符号表.赋值语句(节点.左值, 节点.右值);
    }


    @Override
    public void 离开算术表达式(算术表达式 节点) {
        节点.求值类型 = 符号表.二元运算(节点.左值, 节点.右值);
        日志.信息(节点.运算符 + " 求值类型: " + 节点.求值类型);
    }

    @Override
    public void 离开关系表达式(关系表达式 节点) {
        节点.求值类型 = 符号表.关系运算(节点.左值, 节点.右值);
        日志.信息(节点.运算符 + " 求值类型: " + 节点.求值类型);
    }

    @Override
    public void 离开匹值表达式(匹值表达式 节点) {
        节点.求值类型 = 符号表.匹值运算(节点.左值, 节点.右值);
        日志.信息(节点.运算符 + " 求值类型: " + 节点.求值类型);
    }


    @Override
    public void 离开取负表达式(取负表达式 节点) {
        节点.求值类型 = 符号表.取负(节点);
        日志.信息("- 求值类型: " + 节点.求值类型);
    }

    @Override
    public void 离开取反表达式(取反表达式 节点) {
        节点.求值类型 = 符号表.取反(节点);
        日志.信息("! 求值类型: " + 节点.求值类型);
    }

    @Override
    public void 离开取地址表达式(取地址表达式 节点) {
        节点.求值类型 = new 指针类型(节点.表达式.求值类型);
    }

    @Override
    public void 离开解引用表达式(解引用表达式 节点) {
        节点.求值类型 = 符号表.解引用(节点.表达式);
    }


    @Override
    public void 离开方法调用表达式(方法调用表达式 节点) {
        节点.求值类型 = 符号表.方法调用(节点.方法, 节点.参数);
        日志.信息("行 " + 节点.方法.idToken.getLine() + " 方法调用求值类型: " + 节点.求值类型);
    }


    @Override
    public void 离开成员访问表达式(成员访问表达式 节点) {
        节点.求值类型 = 符号表.成员访问(节点.对象, 节点.成员名);
        日志.信息("行 " + 节点.idToken.getLine() + " 成员访问求值类型: " + 节点.求值类型);
    }


    @Override
    public void 离开标识符(标识符 节点) {
        var 符号 = 节点.作用域.解析(节点.名称);
        日志.信息("行 " + 节点.idToken.getLine() + " 解析ID " + 节点.名称 + " 为 " + 符号);

        if (符号 != null) {
            日志.信息("行 " + 节点.idToken.getLine() + " ID " + 节点.名称 + " 求值类型为 " + 符号.类型);
            节点.求值类型 = 符号.类型;
        } else {
            节点.求值类型 = null;
        }
    }

    @Override
    public void 离开整数型字面量(整数型字面量 节点) {
        节点.求值类型 = 类型索引.整数型.类型;
    }

    @Override
    public void 离开浮点型字面量(浮点型字面量 节点) {
        节点.求值类型 = 类型索引.浮点型.类型;
    }

    @Override
    public void 离开字符型字面量(字符型字面量 节点) {
        节点.求值类型 = 类型索引.字符型.类型;
    }

    @Override
    public void 离开逻辑型字面量(逻辑型字面量 节点) {
        节点.求值类型 = 类型索引.逻辑型.类型;
    }

}
